//
//Copyright (C) 2002-2005  3Dlabs Inc. Ltd.
//All rights reserved.
//
//Redistribution and use in source and binary forms, with or without
//modification, are permitted provided that the following conditions
//are met:
//
//    Redistributions of source code must retain the above copyright
//    notice, this list of conditions and the following disclaimer.
//
//    Redistributions in binary form must reproduce the above
//    copyright notice, this list of conditions and the following
//    disclaimer in the documentation and/or other materials provided
//    with the distribution.
//
//    Neither the name of 3Dlabs Inc. Ltd. nor the names of its
//    contributors may be used to endorse or promote products derived
//    from this software without specific prior written permission.
//
//THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
//"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
//LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
//FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
//COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
//INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
//BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
//LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
//CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
//LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
//ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
//POSSIBILITY OF SUCH DAMAGE.
//

#ifndef _INFOSINK_INCLUDED_
#define _INFOSINK_INCLUDED_

#include "../Include/Common.h"
#include <math.h>

//
// TPrefixType is used to centralize how info log messages start.
// See below.
//
enum TPrefixType {
	EPrefixNone,
	EPrefixWarning,
	EPrefixError,
	EPrefixInternalError,
	EPrefixUnimplemented,
	EPrefixNote
};

enum TOutputStream {
	ENull = 0,
	EDebugger = 0x01,
	EStdOut = 0x02,
	EString = 0x04,
};
//
// Encapsulate info logs for all objects that have them.
//
// The methods are a general set of tools for getting a variety of
// messages and types inserted into the log.
//
class TInfoSinkBase {
public:
	TInfoSinkBase() :
			outputStream(4)
	{
	}
	void erase()
	{
		sink.erase();
	}
	TInfoSinkBase& operator<<(const TPersistString& t)
	{
		append(t);
		return *this;
	}
	TInfoSinkBase& operator<<(char c)
	{
		append(1, c);
		return *this;
	}
	TInfoSinkBase& operator<<(const char* s)
	{
		append(s);
		return *this;
	}
	TInfoSinkBase& operator<<(int n)
	{
		append(String(n));
		return *this;
	}
	TInfoSinkBase& operator<<(const unsigned int n)
	{
		append(String(n));
		return *this;
	}
	TInfoSinkBase& operator<<(float n)
	{
		char buf[40];
		sprintf(buf,
				(fabs(n) > 1e-8 && fabs(n) < 1e8) || n == 0.0f ? "%f" : "%g",
				n);
		append(buf);
		return *this;
	}
	TInfoSinkBase& operator+(const TPersistString& t)
	{
		append(t);
		return *this;
	}
	TInfoSinkBase& operator+(const TString& t)
	{
		append(t);
		return *this;
	}
	TInfoSinkBase& operator<<(const TString& t)
	{
		append(t);
		return *this;
	}
	TInfoSinkBase& operator+(const char* s)
	{
		append(s);
		return *this;
	}
	const char* c_str() const
	{
		return sink.c_str();
	}
	void prefix(TPrefixType message)
	{
		switch (message) {
		case EPrefixNone:
			break;
		case EPrefixWarning:
			append("WARNING: ");
			break;
		case EPrefixError:
			append("ERROR: ");
			break;
		case EPrefixInternalError:
			append("INTERNAL ERROR: ");
			break;
		case EPrefixUnimplemented:
			append("UNIMPLEMENTED: ");
			break;
		case EPrefixNote:
			append("NOTE: ");
			break;
		default:
			append("UNKOWN ERROR: ");
			break;
		}
	}
	void location(TSourceRange range)
	{
		append(FormatSourceRange(range).c_str());
		append(": ");
	}
	void message(TPrefixType message, const char* s)
	{
		prefix(message);
		append(s);
		append("\n");
	}
	void message(TPrefixType message, const char* s, TSourceRange range)
	{
		prefix(message);
		location(range);
		append(s);
		append("\n");
	}

	void setOutputStream(int output = 4)
	{
		outputStream = output;
	}

protected:
	void append(const char *s);

	void append(int count, char c);
	void append(const TPersistString& t);
	void append(const TString& t);

	void checkMem(size_t growth)
	{
		if (sink.capacity() < sink.size() + growth + 2)
			sink.reserve(sink.capacity() + sink.capacity() / 2);
	}
	void appendToStream(const char* s);
	TPersistString sink;
	int outputStream;
};

class TInfoSink {
public:
	TInfoSinkBase info;
	TInfoSinkBase debug;
};

#endif // _INFOSINK_INCLUDED_
